#ifndef RAY_TRACER_H
#define RAY_TRACER_H

#include "stdafx.h"
#include <vector>
#include "SceneObject.h"
#include "Maths.h"


template <typename T>
class RayTracer
{

	Vec3<T> Trace(Ray<T> *ray, vector<GenericObject<T>*> allObjects)
	{
		GenericObject<T> *selectedObject = NULL;
		T t0 = 0, t1 = 0;
		T tnear = 0;
		for(int objectIndex  = 0; objectIndex < allObjects.size(); objectIndex ++)
		{
			GenericObject<T> *currentObject = allObjects[objectIndex];
			if(currentObject->intersect(ray->orig, ray->dir, t0, t1))
			{
				selectedObject = currentObject;
				if(t0 < 0) 
				{		
					t0 = t1;
				}
				tnear = t0;
				break;
			}
		}

		if(selectedObject == NULL)
		{
			return Vec3<T>(0);
		}
		Vec3<T> surfaceColor(0);

		Vec3<T> intersPoint = ray->orig + ray->dir  * tnear;

		for(int objectIndex  = 0; objectIndex < allObjects.size(); objectIndex ++)
		{
			GenericObject<T> *currentObject = allObjects[objectIndex];

			//if its a light
			if(currentObject->emissionColor.x > 0 )
			{
				GenericObject<T> *currentLight = currentObject;
				
				Vec3<T> lightRay = currentObject->center - intersPoint;
				lightRay.normalize();
				Vec3<T> nhit = currentLight->GetIntersectionPointNormal(ray->orig, ray->dir, tnear);
				if (ray->dir.dot(nhit) > 0) 
				{
					nhit = -nhit;
				}

				if(selectedObject->surfaceColor.x > 0)
				{
					T dot = nhit.dot(lightRay);
					if(dot > 0)
					{
						T shading = dot * currentObject->diffuse;
						Vec3<T> tempVec = (selectedObject->surfaceColor * currentLight->emissionColor);
						surfaceColor +=  tempVec * shading;
					}
					
					
				}
				//shadows
				/*for(int secondaryIndex  = 0; secondaryIndex < allObjects.size(); secondaryIndex++)
				{
					if(objectIndex != )
					{

					}
				}*/
			}
		}

		return surfaceColor;

	}

	void render(vector<GenericObject<T>*> allObjects)
	{

		unsigned width = 600, height = 400;
		Vec3<T> *image = new Vec3<T>[width * height], *pixel = image;
		T invWidth = 1 / T(width), invHeight = 1 / T(height);
		T fov = 30, aspectratio = width / T(height);
		T angle = tan(3.141592653589793 * 0.5 * fov / T(180));
		std::ofstream myFile("./genericObjects.txt");
		
		// Trace rays
		for (unsigned y = 0; y < height; ++y) {
			for (unsigned x = 0; x < width; ++x, ++pixel) {
				T xx = (2 * ((x + 0.5) * invWidth) - 1) * angle * aspectratio;
				T yy = (1 - 2 * ((y + 0.5) * invHeight)) * angle;
				Vec3<T> rayDir(xx, yy, -1);
				rayDir.normalize();

				Ray<T> *currentRay = new Ray<T>(Vec3<T>(0), rayDir);

				Vec3<T> output1 =  Trace(currentRay, allObjects);
				//Vec3<T> output2 = RecursiveRayTracer<T>::trace(Vec3<T>(0), rayDir,spheres,0);

				*pixel = output1;
			}
		}
		

		myFile.close();
		// Save result to a PPM image (keep these flags if you compile under Windows)
		//	std::ofstream ofs("./untitled.ppm", std::ios::out | std::ios::binary);
		//	ofs << "P6\n" << width << " " << height << "\n255\n";
		//	for (unsigned i = 0; i < width * height; ++i) {
		//		ofs << (unsigned char)(std::min(T(1), image[i].x) * 255) << 
		//		(unsigned char)(std::min(T(1), image[i].y) * 255) <<
		//		(unsigned char)(std::min(T(1), image[i].z) * 255); 
		//	}
		//	ofs.close();
	

		std::ofstream ofs("./dragon.ppm", std::ios::out | std::ios::binary);
		ofs << "P6\n" << width << " " << height << "\n255\n";
		for (unsigned i = 0; i < width * height; ++i) {
			ofs << (unsigned char)(min(T(1), image[i].x) * 255) << 
			(unsigned char)(min(T(1), image[i].y) * 255) <<
			(unsigned char)(min(T(1), image[i].z) * 255); 
		}
		ofs.close();
		delete [] image;
	}

	public:

	void Execute(void)
	{
		vector<GenericObject<float>*> objectList;
		time_t startTimer;
		time_t endTimer;
		
		time(&startTimer);
		//vector<Triangle<float>*> objectMesh = ObjectParser::ParseObjFile("C:\\Users\\adebarro\\Desktop\\dragon.obj",Vec3<float>(5, -1, -15) );
		time(&endTimer);
		
		std::cout << "elapsed time is : " << endTimer - startTimer << std::endl;

		//center,T radius, Vec3<T> surfaceColor, Vec3<T> emissonColor,T transparency , T reflection, int type)
		//objectList.push_back(new GenericObject<float>(Vec3<float>(0, 0, -2), Vec3<float>(0.90, 0.76, 0.46),Vec3<float>(0), 0, 0.0, "C:\\Users\\Adrian\\Desktop\\dragon.obj"));
		//objectList.push_back(new GenericObject<float>(Vec3<float>(0, -10004, -20), 10000, Vec3<float>(0.2),0, 0, 0.0,0));
		//objectList.push_back(new GenericObject<float>(Vec3<float>(0, 0, -20), 4, Vec3<float>(1.00, 0.32, 0.36),1, 0.5,0));
		//objectList.push_back(new GenericObject<float>(Vec3<float>(5, -1, -15), 2, Vec3<float>(0.90, 0.76, 0.46), 1, 0.0,0));
		//objectList.push_back(new GenericObject<float>(Vec3<float>(5, 0, -25), 3, Vec3<float>(0.65, 0.77, 0.97), 1, 0.0,0));
		//objectList.push_back(new GenericObject<float>(Vec3<float>(-5.5, 0, -15), 3, Vec3<float>(0.90, 0.90, 0.90), 1, 0.0, 0));
		////light
		//objectList.push_back(new GenericObject<float>(Vec3<float>(0, 5,5), 0.1, Vec3<float>(0), 0, 0.0,Vec3<float>(3),2));
		
		vector<Triangle<T> *> trianglesVector;

		GenericObject<T> *bigSphere = new GenericObject<T>();
		bigSphere->SetCenter(Vec3<T>(0, -10004, -20));
		bigSphere->radius = 10000;
		bigSphere->SetReflection(0.6);
		bigSphere->diffuse = 0.4;
		bigSphere->SetSurfaceColor(Vec3<T>(0.7,0.7,0.7));


		GenericObject<T> *smallSphere = new GenericObject<T>();
		smallSphere->SetCenter(Vec3<T>(5, -1,-15));
		smallSphere->radius = 2;
		smallSphere->SetReflection(0.6);
		smallSphere->diffuse = 0.6;
		smallSphere->SetSurfaceColor(Vec3<T>(0.7,0.7,1.0));
		
		GenericObject<T> *light1 = new GenericObject<T>();
		light1->SetCenter(Vec3<float>(0,5,5));
		light1->radius = 2;
		light1->SetEmissionColor(Vec3<float>(0.7,0.7,0.7));
		light1->CreateSceneObject(0, trianglesVector);

		GenericObject<T> *light2 = new GenericObject<T>();
		light2->SetCenter(Vec3<float>(0,5,5));
		light2->radius = 0.1;
		light1->SetEmissionColor(Vec3<float>(0.7f,0.7f, 0.9f));
		light2->CreateSceneObject(0, trianglesVector);

		objectList.push_back(bigSphere);
		objectList.push_back(smallSphere);
		objectList.push_back(light1);
		objectList.push_back(light2);
		
		time(&startTimer);

		std::cout << "start time is " << startTimer << std::endl;
		
		render(objectList);
		
		time(&endTimer);
		std::cout << "End Time " << endTimer << std::endl;
		std::cout << "elapsed time is : " << endTimer - startTimer << std::endl;


		while (!objectList.empty()) {
			GenericObject<float> *sph = objectList.back();
			objectList.pop_back();
			delete sph;
		}

		/*while (!spheres.empty()) {
			Sphere<float> *sph = spheres.back();
			spheres.pop_back();
			delete sph;
		}*/
	}

};

#endif // DEBUG
